Hate Crimes as Reported to the FBI in 13 Cities of Interest¶

Analysis by Quintessa Guengerich for Strong Cities Network¶

Fall 2024¶

Return to Home

Goal: Gain an understanding of the "hate landscape" in 13 American cities.

In this file, hate crime data from 1991 to 2022 from the FBI crime dataset is cleaned and visualized. (Note: Data from 2023 is present, but none of the relevant cities reported data in 2023). Data downloaded previously using the Census Bureau API is also used to visualize the demographics of each city.

There are 13 cities of interest to Strong Cities Network:

  • Akron, Ohio
  • Albuquerque, New Mexico
  • Athens, Ohio
  • Boise, Idaho
  • Chattanooga, Tennessee
  • Columbus, Ohio
  • Mount Vernon, New York
  • Overland Park, Kansas
  • Reno, Nevada
  • Rochester, New York
  • Seattle, Washington
  • Spring Lake, North Carolina
  • Stamford, Connecticut

Furthermore, each city has received a hate crime reporting grade, based on the ease of reporting a hate crime in that city. This grade is a somewhat feeble attempt to differentiate cities from one another when hate crime is generally known to be underreported.1

A Note on Hate Landscapes¶

The state of this dataset does not lend itself to comparing these cities' hate landscapes to each other, and thus no graphics comparing cities has been provided. Instead, each city can be viewed holistically, with hate crimes over time and by demographic group, in particular, seen through the lens that the available datapoints very well may be an unrepresentative sample of the truth.

1 Kena, Grace. “Hate Crime Victimization, 2005–2019.” National Crime Victimization Survey, September 2021, 24.

Data Source: https://cde.ucr.cjis.gov/LATEST/webapp/#

Import Necessary Libraries¶

In [1]:
# Importing necessary libraries for data manipulation and visualization
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
import plotly.io as pio

# Allow export to html while preserving plotly
pio.renderers.keys()
pio.renderers.default = 'notebook'

Import and Clean Data¶

In [2]:
# Selecting relevant columns from the dataset
# Loading the FBI hate crime dataset
# FBI hate crime incidents in the United States
fbi_data_all_cols = pd.read_csv("data/hate_crime.csv")
In [3]:
# Selecting relevant columns from the dataset
# Renaming columns for better readability
fbi_data = fbi_data_all_cols[['data_year', 'pug_agency_name', 'state_abbr', 'bias_desc']].copy()
fbi_data.rename(columns={'data_year': 'Year', 'pug_agency_name': 'Police Agency Name', 'state_abbr': 'State Abbr.', 'bias_desc': 'Bias Description'}, inplace=True)
In [4]:
# Extract all bias categories in the "Bias Description" column
individual_bias_categories = fbi_data[~fbi_data['Bias Description'].str.contains(';')]
individual_bias_categories = individual_bias_categories['Bias Description'].unique()
individual_bias_categories = individual_bias_categories.tolist()

# Hate Crime categorizations, previously extracted by hand
anti_religious = ['Anti-Jewish','Anti-Catholic','Anti-Protestant','Anti-Other Religion','Anti-Islamic (Muslim)',
                  'Anti-Multiple Religions, Group', 'Anti-Atheism/Agnosticism','Anti-Sikh','Anti-Other Christian',
                  'Anti-Hindu','Anti-Eastern Orthodox (Russian, Greek, Other)',"Anti-Jehovah's Witness",'Anti-Church of Jesus Christ','Anti-Buddhist']
anti_race = ['Anti-Black or African American', 'Anti-White','Anti-Arab', 'Anti-Asian','Anti-Hispanic or Latino','Anti-Multiple Races, Group',
             'Anti-Other Race/Ethnicity/Ancestry', 'Anti-American Indian or Alaska Native','Anti-Native Hawaiian or Other Pacific Islander']
anti_disability = ['Anti-Physical Disability', 'Anti-Mental Disability']
anti_lgbtq = ['Anti-Gay (Male)', 'Anti-Lesbian (Female)', 'Anti-Heterosexual', 'Anti-Lesbian, Gay, Bisexual, or Transgender (Mixed Group)', 'Anti-Bisexual',
              'Anti-Gender Non-Conforming', 'Anti-Transgender']
anti_sexbased = ['Anti-Male', 'Anti-Woman']

# Dictionary to map biases to their respective categories
bias_category_map = {
    'Disability-Based Hate': anti_disability,
    'LGBTQ+ Based Hate': anti_lgbtq,
    'Race-Based Hate': anti_race,
    'Sex-Based Hate': anti_sexbased,
    'Religious-Based Hate': anti_religious
}
In [5]:
# All city names
cities = ['akron', 'albuquerque', 'athens', 'boise', 'chattanooga', 'columbus', 'mountvernon', 
          'overlandpark', 'reno', 'rochester', 'seattle', 'springlake', 'stamford']

# PUG Agencies of Interest per City
pug_agencies = {
    'akron': ['Akron', 'University of Akron'],
    'albuquerque': ['Albuquerque', 'University of New Mexico'],
    'athens': ['Athens', 'Nelsonville', 'Albany', 'Glouster'],
    'boise': ['Boise'],
    'chattanooga': ['Chattanooga', 'Chattanooga State Community College'],
    'columbus': ['Columbus', 'Columbus State Community College', 'Port Columbus International Airport'],
    'mountvernon': ['Mount Vernon'], 
    'overlandpark': ['Overland Park'],
    'reno': ['Reno', 'Reno Tahoe Airport Authority'],
    'rochester': ['Rochester'],
    'seattle': ['Seattle', 'Port of Seattle'],
    'springlake': ['Spring Lake', 'Fayetteville'],
    'stamford': ['Stamford']
}
In [6]:
# Define color palettes used in graphics below

anti_color_map = {'Race-Based Hate': 'rgb(124, 124, 124)',
             'LGBTQ+ Based Hate': 'rgb(160, 97, 119)',
             'Disability-Based Hate':'rgb(140, 120, 93)',
             'Religious-Based Hate': 'rgb(104, 133, 92)',
             'Sex-Based Hate': 'rgb(224, 219, 119)',
             'Anti-White': 'rgb(133, 92, 117)',
             'Anti-Black or African American': 'rgb(217, 175, 107)',
             'Anti-Asian':'rgb(115, 111, 76)',
             'Anti-Hispanic or Latino': 'rgb(98, 83, 119)',
             'Anti-American Indian or Alaska Native': 'rgb(175, 100, 88)',
             'Anti-Native Hawaiian or Other Pacific Islander':'rgb(82, 106, 131)'}

Define Functions for Sorting and Counting Biases¶

In [7]:
def count_biases(all_data, city_name, year=None):
    """
    Count the number of biases reported for a given city for a given year.

    Parameters:
    all_data (DataFrame): The full dataset containing hate crime records.
    city_name (str): The name of the city to filter the data for.
    year (int, optional): The year to filter by. If not provided, data for all years is used.

    Returns:
    int: Total number of bias incidents for the specified city (and year, if provided).
    """
    city_data = all_data[all_data['Police Agency Name'].isin(pug_agencies[city_name])]
    if year is not None:
        city_data = city_data[city_data['Year'] == year]

    data = []

    # Iterate over each bias and classify it into its category
    for bias in individual_bias_categories:
        contains_bias = city_data[city_data['Bias Description'].str.contains(bias, regex=False)]
        count = contains_bias.shape[0]
        
        # Determine the category for the bias
        for category, biases in bias_category_map.items():
            if bias in biases:
                data.append({
                    'Category': category,
                    'Subcategory': bias,
                    'Counts': count
                })
                break

    # Create a DataFrame from the data list
    df = pd.DataFrame(data)
    if year is not None:
        df['Year'] = year
    df['City'] = city_name
    return df

def count_biases_all_years(all_data, city_name):
    """
    Count the number of biases reported for a given city for all years.

    Parameters:
    all_data (DataFrame): The full dataset containing hate crime records.
    city_name (str): The name of the city to filter the data for.

    Returns:
    int: Total number of bias incidents for the specified city (and year, if provided).
    """
    data = pd.DataFrame()
    for year in all_data['Year'].unique():
        df_biases = count_biases(fbi_data, city_name, year)
        data = pd.concat([data, df_biases], axis=0)
    return data

def count_biases_all_cities_all_years(all_data):
    """
    Count the number of biases reported for a ll cities and all years.

    Parameters:
    all_data (DataFrame): The full dataset containing hate crime records.

    Returns:
    int: Total number of bias incidents for the specified city (and year, if provided).
    """
    all_data = pd.DataFrame()
    for city in cities:
        df = count_biases_all_years(fbi_data, city)
        all_data = pd.concat([all_data, df], axis=0)
    return all_data
In [8]:
counted_biases = count_biases_all_cities_all_years(fbi_data)
In [9]:
# Loading the FBI hate crime dataset
cities_census_data = pd.DataFrame()

for city in cities:
    city_data = pd.read_csv("data/" + city + "/" + city + "_census_data.csv", index_col=0)
    city_data['City'] = city
    cities_census_data = pd.concat([cities_census_data, city_data], axis=0, ignore_index=True)
In [10]:
# Using data beyond 2008 because those are the years for which census data is available.
hate_crimes_per_capita = pd.merge(left=counted_biases[counted_biases['Year'] > 2008], right=cities_census_data, on=['City', 'Year'], how='left')

Define Plotting Functions¶

In [11]:
# Importing necessary libraries for data manipulation and visualization
import plotly.express as px
from plotly.subplots import make_subplots
import plotly.graph_objects as go

def get_relevant_category(id_str):
    """
    Extracts a relevant category from a string based on slashes.

    Parameters:
    id_str (str): A string representing the full ID path, where categories are separated by slashes ('/').

    Returns:
    str: The relevant category or None if no relevant category is found.
    """
    parts = id_str.split('/')
    if len(parts) > 2:
        return parts[2]
    elif len(parts) > 1:
        return parts[1]
    else:
        return None

def make_treeplot(data, city, fig, row, col, value_col, title_col, hover_template, root_color='white'):
    """
    Creates and adds a treemap plot to a subplot.

    Parameters:
    data (DataFrame): DataFrame containing the data to be visualized.
    city (str): Name of the city to display in the treemap.
    fig (Figure): The figure object to which the treemap will be added.
    row (int): The row index of the subplot in which to add the treemap.
    col (int): The column index of the subplot in which to add the treemap.
    value_col (str): The column in the DataFrame used to size the treemap segments.
    title_col (str): The column used to define the hierarchy (e.g., 'Category', 'Subcategory').
    hover_template (str): The hover template used to customize the hover text.
    root_color (str, optional): The color of the root node of the treemap. Default is 'white'.

    Returns:
    None: The function adds the plot to the figure in the specified row and column.
    """
    tree_fig = px.treemap(data, 
                          path=[px.Constant(str.title(city)), title_col, 'Subcategory'], 
                          values=value_col, 
                          color=title_col)

    tree_fig.data[0].marker.colors = [anti_color_map.get(get_relevant_category(id_str), None) for id_str in tree_fig.data[0].ids]
    tree_fig = go.Figure(tree_fig)
    tree_fig.data[0].root.color = root_color

    tree_fig.update_traces(
        hovertemplate=hover_template,
        textfont=dict(size=16), marker=dict(cornerradius=5)
    )
    
    fig.add_trace(tree_fig.data[0], row=row, col=col)

def create_city_summary(data, city, state, reporting_grade=None, reporting_grade_explanation=None):
    """
    Creates a detailed city summary of hate crime statistics, including plots for counts, per capita data, and population distributions.

    Parameters:
    data (DataFrame): The full dataset containing hate crime records, with columns for counts and populations.
    city (str): Name of the city to generate the summary for.
    state (str): State where the city is located.
    reporting_grade (str, optional): Grade for how well the city reports hate crimes. Default is None.
    reporting_grade_explanation (str, optional): Explanation of the reporting grade. Default is None.

    Returns:
    None: Displays the city summary, including multiple plots with data visualizations.
    """

    # All cities of interest did not report data in 2023, so let's just focus on prior years.
    data = data[(data['City'] == city) & (data['Year'] < 2023)].copy()

    data['Hate Crime Per 10,000 People'] = data['Counts']/data['Total Population']*10000

    
    overall_per_capita = data.groupby(['City', 'Year']).agg({'Counts': 'sum', 'Total Population': 'first'})
    overall_per_capita['Hate Crime Per 10,000 People'] = overall_per_capita['Counts']/overall_per_capita['Total Population']*10000

    # Create area for all subplots
    height = (1-0.15)/3
    fig = make_subplots(rows=4, cols=3, 
                        specs=[[{'type': 'pie', 'rowspan': 1, 'colspan': 1}, {'type': 'scatter'}, {'type': 'scatter'}], 
                               [{'type': 'scatter', 'rowspan': 1, 'colspan': 3}, {'type': 'scatter'}, {'type': 'scatter'}],
                               [{'type': 'domain', 'colspan': 3}, {'type': 'domain'}, {'type': 'domain'}], 
                               [{'type': 'domain', 'colspan': 3}, {'type': 'domain'}, {'type': 'domain'}]],
                        column_widths=[0.5,0.5,0.5],
                        row_heights=[0.15,height,height,height])
    
    # Plot hate crimes over time
    lineplot = px.line(overall_per_capita.reset_index(), x="Year", y="Hate Crime Per 10,000 People", color_discrete_sequence=['black'])
    lineplot = go.Figure(lineplot)
    fig.add_trace(lineplot.data[0], row=2, col=1)

    # Create tree plot of hate crimes by bias category, represented as raw counts
    make_treeplot(data, city, fig, row=3, col=1, value_col='Counts', title_col='Category', hover_template='<b>%{label}</b><br>Count: %{value}<extra></extra>')

    # All subcategories, per capita
    race_subcats = data[data['Category'] == 'Race-Based Hate'].copy()
    race_subcats['Race Population'] = 0.0
    race_subcats.loc[race_subcats['Subcategory'] == 'Anti-Black or African American', 'Race Population'] = race_subcats['Black or African American']
    race_subcats.loc[race_subcats['Subcategory'] == 'Anti-White', 'Race Population'] = race_subcats['White']
    race_subcats.loc[race_subcats['Subcategory'] == 'Anti-Asian', 'Race Population'] = race_subcats['Asian']
    race_subcats.loc[race_subcats['Subcategory'] == 'Anti-Hispanic or Latino', 'Race Population'] = race_subcats['Hispanic or Latino (of any race)']
    race_subcats.loc[race_subcats['Subcategory'] == 'Anti-American Indian or Alaska Native', 'Race Population'] = race_subcats['American Indian and Alaska Native']
    race_subcats.loc[race_subcats['Subcategory'] == 'Anti-Native Hawaiian and Other Pacific Islander', 'Race Population'] = race_subcats['Native Hawaiian and Other Pacific Islander']
    race_subcats = race_subcats[(race_subcats['Subcategory'] != "Anti-Other Race/Ethnicity/Ancestry") & (race_subcats['Subcategory'] != "Anti-Arab") &
                                (race_subcats['Subcategory'] != "Anti-Multiple Races, Group")]

    race_subcats['Hate Crime Per Group (Per 10,000)'] = race_subcats['Counts']/race_subcats['Race Population']*10000

    # Using per capita data, plot hate crimes by bias category 
    make_treeplot(race_subcats, city, fig, row=4, col=1, value_col='Hate Crime Per Group (Per 10,000)', title_col='Category',
                  hover_template='<b>%{label}</b><br>Per Capita: %{value}<extra></extra>', root_color='white')

    race_subcats = race_subcats.groupby('Subcategory').agg({'Hate Crime Per Group (Per 10,000)': 'mean'}).reset_index()

    # Population Pie Chart
    custom_colors = list(px.colors.qualitative.Set3)
    custom_colors[5] = '#FFFFFF'
    data_2022 = data[data['Year'] == 2022]
    races_2022 = data_2022[['White', 'Black or African American',
        'American Indian and Alaska Native', 'Asian',
        'Native Hawaiian and Other Pacific Islander',
        'Hispanic or Latino (of any race)']].iloc[0]
    races_2022 = pd.DataFrame(races_2022)
    pie_fig = px.pie(races_2022, values=races_2022.columns[0], names=races_2022.index, hole=0.7)

    # Update the traces to center annotation and adjust internal legend position
    pie_fig.update_traces(
        hovertemplate='<b>%{label}</b><br>%{value}<extra></extra>',
        textposition='outside',  # Move the labels outside the pie chart
        marker=dict(colors=px.colors.qualitative.Antique)
    )

    # Add annotations
    annotations = [dict(text="Hate Crimes as Reported to the FBI in " + str.title(city) + ", " + state, font=dict(family="Arial", color="darkgray"),
                        x=-0.03, y=1.11, font_size=36, showarrow=False, xref='paper', yref='paper'),
                   dict(text=f'{int(data_2022["Total Population"].iloc[0]):,}', x=0.11, y=0.95, font_size=20, showarrow=False, xref='paper', yref='paper'),
                   dict(text='Population of ' + str.title(city) + ' in 2022', x=0, y=1.05, font_size=16, showarrow=False, xref='paper', yref='paper'),
                   dict(text="Race-Based Hate Crimes in " + str.title(city) + " Per Group Capita from 2009-2022", x=0, y=0.235, font_size=16, showarrow=False, xref='paper', yref='paper'),
                   dict(text="All Reported Hate Crimes in " + str.title(city) + " from 2009-2022", x=0, y=0.535, font_size=16, showarrow=False, xref='paper', yref='paper'),
                   dict(text="All Reported Hate Crimes in " + str.title(city) + " Per 10,000 People, Over Time", x=0, y=0.83, font_size=16, showarrow=False, xref='paper', yref='paper'),
                   dict(text="REPORTING GRADE", font=dict(family="Arial", size=12, color="darkgray"), x=0.72, y=1.03, xref='paper', yref='paper', showarrow=False)]

    # Add Reporting Grade and note if exists
    if reporting_grade is not None:
        annotations.append(dict(text=str(reporting_grade),font=dict(family="Arial Black", color="black"), x=0.74, y=1.03, font_size=150, showarrow=False, xref='paper', yref='paper'))
        if reporting_grade_explanation is not None:
            annotations.append(dict(text=reporting_grade_explanation, x=1.0, y=0.98, font=dict(family="Arial", color="darkgray"), font_size=12, showarrow=False, xref='paper', yref='paper'))
    else:
        annotations.append(dict(text="--", font=dict(family="Arial Black", color="darkgray"), x=0.74, y=1.03, font_size=150, showarrow=False, xref='paper', yref='paper'))

    # Update layout to center annotation and manage legend automatically
    fig.add_trace(pie_fig.data[0], row=1, col=1)
    fig.update_layout(width=1400, height=1800,
        annotations=annotations,
        margin=dict(t=200),  # Adjust margins for the legend
        legend=dict(x=0.26, y=0.95, xanchor="left", yanchor="middle", orientation="v", traceorder="normal"),
    )

    fig.show()

Comparing Hate Crime Reporting Grade¶

Each city has received a Reporting Score, on a scale of 1-5:

5: City has an office dedicated to hate crime reporting and/or prevention.

4: City does not have an office dedicated to hate crime, but does explicitly provide instructions for hate crime reporting.

3: City does not provide specific instructions for reporting hate crimes, but a third party entity (like a local university) has provided them.

2: City does not have instructions easily available for hate crime reporting, but they do have general information for reporting a crime.

1: City does not have clear instructions for reporting a crime.

In [12]:
akron_reporting_grade = 5
athens_reporting_grade = 2
albuquerque_reporting_grade = 5
boise_reporting_grade = 3
chattanooga_reporting_grade = 5
columbus_reporting_grade = 0
mountvernon_reporting_grade = 4
overlandpark_reporting_grade = 2
rochester_reporting_grade = 3
seattle_reporting_grade = 5
springlake_reporting_grade = 1
stamford_reporting_grade = 3
In [13]:
hate_crimes_per_capita['Reporting Grade'] = akron_reporting_grade
hate_crimes_per_capita.loc[hate_crimes_per_capita['City'] == 'akron', 'Reporting Grade'] = akron_reporting_grade
hate_crimes_per_capita.loc[hate_crimes_per_capita['City'] == 'athens', 'Reporting Grade'] = athens_reporting_grade
hate_crimes_per_capita.loc[hate_crimes_per_capita['City'] == 'albuquerque', 'Reporting Grade'] = albuquerque_reporting_grade
hate_crimes_per_capita.loc[hate_crimes_per_capita['City'] == 'boise', 'Reporting Grade'] = boise_reporting_grade
hate_crimes_per_capita.loc[hate_crimes_per_capita['City'] == 'chattanooga', 'Reporting Grade'] = chattanooga_reporting_grade
hate_crimes_per_capita.loc[hate_crimes_per_capita['City'] == 'columbus', 'Reporting Grade'] = columbus_reporting_grade
hate_crimes_per_capita.loc[hate_crimes_per_capita['City'] == 'mountvernon', 'Reporting Grade'] = mountvernon_reporting_grade
hate_crimes_per_capita.loc[hate_crimes_per_capita['City'] == 'overlandpark', 'Reporting Grade'] = overlandpark_reporting_grade
hate_crimes_per_capita.loc[hate_crimes_per_capita['City'] == 'reno', 'Reporting Grade'] = overlandpark_reporting_grade
hate_crimes_per_capita.loc[hate_crimes_per_capita['City'] == 'rochester', 'Reporting Grade'] = rochester_reporting_grade
hate_crimes_per_capita.loc[hate_crimes_per_capita['City'] == 'seattle', 'Reporting Grade'] = seattle_reporting_grade
hate_crimes_per_capita.loc[hate_crimes_per_capita['City'] == 'springlake', 'Reporting Grade'] = springlake_reporting_grade
hate_crimes_per_capita.loc[hate_crimes_per_capita['City'] == 'stamford', 'Reporting Grade'] = stamford_reporting_grade
overall_per_capita = hate_crimes_per_capita.groupby(['City', 'Year']).agg({'Counts': 'sum', 'Total Population': 'first', 'Reporting Grade': 'first'})
overall_per_capita['Hate Crime Per 10,000 People'] = overall_per_capita['Counts']/overall_per_capita['Total Population']*10000
all_hate_crimes_since_2019 = overall_per_capita[overall_per_capita.index.get_level_values('Year') > 2020]
all_hate_crimes_since_2019 = all_hate_crimes_since_2019.groupby('City').agg({'Reporting Grade': 'first', 'Hate Crime Per 10,000 People': 'mean'})
all_hate_crimes_since_2019.reset_index(inplace=True)

Data Summary¶

In [14]:
city_summary_table_2023 = hate_crimes_per_capita.groupby('City').agg({'Reporting Grade': 'last', 'Counts': 'sum', 'Total Population': 'mean', 
                                                                       'White': 'mean', 'Black or African American': 'mean', 'Asian': 'mean',
                                                                      'American Indian and Alaska Native': 'mean', 'Native Hawaiian and Other Pacific Islander': 'mean'})
city_summary_table_2023['Hate Crime Per 10,000 People'] = city_summary_table_2023['Counts']/city_summary_table_2023['Total Population']*10000
city_summary_table_2023.drop(columns=['Counts'], inplace=True)
city_summary_table_2023['White'] = city_summary_table_2023['White']/city_summary_table_2023['Total Population']*100
city_summary_table_2023['Black or African American'] = city_summary_table_2023['Black or African American']/city_summary_table_2023['Total Population']*100
city_summary_table_2023['American Indian and Alaska Native'] = city_summary_table_2023['American Indian and Alaska Native']/city_summary_table_2023['Total Population']*100
city_summary_table_2023['Native Hawaiian and Other Pacific Islander'] = city_summary_table_2023['Native Hawaiian and Other Pacific Islander']/city_summary_table_2023['Total Population']*100
city_summary_table_2023['Asian'] = city_summary_table_2023['Asian']/city_summary_table_2023['Total Population']*100
city_summary_table_2023
Out[14]:
Reporting Grade Total Population White Black or African American Asian American Indian and Alaska Native Native Hawaiian and Other Pacific Islander Hate Crime Per 10,000 People
City
akron 5 198628.214286 64.911833 33.574391 3.900000 1.124499 0.061313 6.192474
albuquerque 5 550540.000000 75.408638 4.401821 3.932749 6.004961 0.238441 4.559160
athens 2 64570.000000 93.734817 3.630390 3.560698 1.233213 0.079869 33.142326
boise 3 217456.428571 92.452150 2.246197 4.636791 1.810478 0.376890 7.219837
chattanooga 5 174021.571429 62.531246 34.444909 2.666541 0.805770 0.109305 4.769524
columbus 0 832568.142857 64.086320 30.751228 5.810738 1.207477 0.138281 14.629433
mountvernon 4 68582.500000 25.244362 65.449537 2.781218 1.170436 0.149247 11.373164
overlandpark 2 182822.071429 86.309210 5.731216 8.289363 0.995464 0.109162 2.078524
reno 2 237166.785714 80.351501 4.076516 8.404815 2.165565 1.326884 4.764579
rochester 3 209790.071429 49.786879 43.455413 3.805539 1.680149 0.188283 7.531338
seattle 5 665215.214286 74.165568 9.016631 18.070865 2.137149 0.813754 28.126236
springlake 1 325405.714286 55.568894 39.857145 3.679824 3.221188 0.698600 3.534050
stamford 3 126802.142857 63.938025 15.851974 8.952643 0.657999 0.074019 6.624494

City Visualizations¶

Akron¶

When you google "Hate Crime Reporting Akron, OH": https://www.ideastream.org/community/2023-12-05/akron-police-implements-safe-place-program-to-help-victims-of-hate-crimes

In 2023, Akron began implementing tthe similar hate crime reporting method as Seattle. Grade: 5

In [15]:
create_city_summary(hate_crimes_per_capita, "akron", "OH", akron_reporting_grade, "Akron, Ohio has a dedicated office for hate crime<br>reporting and prevention.")

Athens¶

When you google "Hate Crime Reporting Athens Ohio", you get: https://www.ci.athens.oh.us/120/Police-Department

They don't have a specific hate crime reporting center. Grade: 2

In [16]:
create_city_summary(hate_crimes_per_capita, "athens", "OH", athens_reporting_grade)

Albuquerque¶

When you google "Hate Crime Reporting Albuquerque", you get: https://www.cabq.gov/office-of-equity-inclusion/resources-1/hate-crimes

Albuquerque's Office of Equity and Inclusion has a guide on hate crime reporting, in many languages. They have partnered with the FBI. Grade: 5

In [17]:
create_city_summary(hate_crimes_per_capita, "albuquerque", "NM", albuquerque_reporting_grade, "Albuquerque's Office of Equity and Inclusion<br>has a guide on hate crime reporting,<br>in many languages.")

Boise¶

When you google "Hate Crime Reporting Boise Idaho" you get: https://www.uidaho.edu/-/media/uidaho-responsive/files/law/academics/practical-skills/clinics/immigration/reporting-bias-and-hate-crimes-idaho.pdf?la=en&rev=b5fb334643604dc5adf305fcdf8df59a

The city itself does not have a specific page for hate crime reporting, but the University of Idaho does. Grade: 3

In [18]:
create_city_summary(hate_crimes_per_capita, "boise", "ID", boise_reporting_grade, "Boise does not have a specific page for <br>hate crime reporting, but the University of Idaho does.")

Chattanooga¶

When you Google "Hate Crime Reporting in Chattanooga, TN": https://connect.chattanooga.gov/wp-content/uploads/2020/02/Law-Enforcement-Response-to-Hate-Crime-in-Chattanooga-2_2020-2.pdf

Chattanooga, TN report states: "With those words in his April 2018 State of the City speech, Mayor Andy Berke announced the creation of the Council Against Hate." Grade: 5

In [19]:
create_city_summary(hate_crimes_per_capita, "chattanooga", "TN", chattanooga_reporting_grade, "Chattanooga established a Council Against Hate <br> in 2018.")

Columbus¶

When you google "Hate Crime Reporting Columbus, OH" you get: https://www.columbus.gov/Services/Public-Safety/Find-a-Police-Report/File-Offense

Columbus actually gets a 0 -- because they have instructions for reporting crimes in general, stating that you cannot report a crime if there is evidence it is motivated by hate. Grade: 0

In [20]:
create_city_summary(hate_crimes_per_capita, "columbus", "OH", columbus_reporting_grade, "Columbus's crime reporting webpage does not allow<br>reporting hate crimes.")

Mount Vernon¶

When you google "Hate Crime Reporting in Mount Vernon, NY" : https://humanrights.westchestergov.com/file-an-incident-of-hate-bias-or-discrimination

Westchester County has a whole page, although the city of Mount Vernon does not. Grade: 4

In [21]:
create_city_summary(hate_crimes_per_capita, "mountvernon", "NY", mountvernon_reporting_grade, "Westchester County has a page dedicated to hate crime,<br>but Mount Vernon does not.")

Overland Park¶

When you google "Hate Crime Reporting Overland Park KS": https://humanrights.westchestergov.com/file-an-incident-of-hate-bias-or-discrimination

They have general crime reporting, but no mention of hate crime: 2

In [22]:
create_city_summary(hate_crimes_per_capita, "overlandpark", "KS", overlandpark_reporting_grade, "Overland Park has easy access to general<br>crime reporting, but makes no mention of hate crime.")

Reno¶

When you google "Hate Crime Reporting Reno NV": https://renopd.com/report

Reno has a page to file a police report but no mention of hate crime: 2

In [23]:
reno_reporting_grade = 2
In [24]:
create_city_summary(hate_crimes_per_capita, "reno", "NV", reno_reporting_grade)

Rochester¶

When you google "Hate Crime Reporting Rochester, NY": https://www.ny.gov/hate-crimes-task-force/new-york-state-anti-hate-crime-resource-guide

The state of New York has a new hate crime reporting and prevention program -- but this county/city does not. Grade: 3

In [25]:
create_city_summary(hate_crimes_per_capita, "rochester", "NY", rochester_reporting_grade)

Seattle¶

When you google "Hate Crime Reporting Seattle": https://www.seattle.gov/spd-safe-place/report-a-hate-crime

Seattle has a hate crime reporting office. Grade: 5

In [26]:
create_city_summary(hate_crimes_per_capita, "seattle", "WA", seattle_reporting_grade, "Seattle has established an office dedicated<br>to hate crimes.")

Spring Lake¶

When you google "Hate Crime Reporting Spring Lake NC": https://www.townofspringlake.com/police-tips/

They don't even have a page for reporting a crime, only police tips. Grade: 1

In [27]:
create_city_summary(hate_crimes_per_capita, "springlake", "NC", springlake_reporting_grade)

Stamford¶

When you google "Hate Crime Reporting Stamford CT": https://portal.ct.gov/hatecrimes?language=en_US

The state of Connecticut has a hate crimes council, but the city does not. Grade: 3

In [28]:
create_city_summary(hate_crimes_per_capita, "stamford", "CT", stamford_reporting_grade)